#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <error.h>
#include <sys/epoll.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#define SERVER_PORT 8888
#define MAX_LISTEN 128
#define LOCAL_IPADDRESS "0.0.0.0"
#define BUFFER_SIZE 128
#define READ_SIZE 5
int main()
{
    /* 创建socket套接字 */
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket error");
        exit(-1);
    }

    /* 设置端口复用 */
    int enableOpt = 1;
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&enableOpt, sizeof(enableOpt));
    if (ret == -1)
    {
        perror("setsockopt error");
        exit(-1);
    }

    /* 绑定 */
#if 0
    /* 这个结构体不好用 */
    struct sockaddr localAddress;
#else
    struct sockaddr_in localAddress;
#endif
    /* 清除脏数据 */
    memset(&localAddress, 0, sizeof(localAddress));

    /* 地址族 */
    localAddress.sin_family = AF_INET;
    /* 端口需要转成大端 */
    localAddress.sin_port = htons(SERVER_PORT);
    /* ip地址需要转成大端 */

    /* Address to accept any incoming messages.  */
    /* INADDR_ANY = 0x00000000 */
    localAddress.sin_addr.s_addr = htonl(INADDR_ANY);

    int localAddressLen = sizeof(localAddress);
    ret = bind(sockfd, (struct sockaddr *)&localAddress, localAddressLen);
    if (ret == -1)
    {
        perror("bind error");
        exit(-1);
    }

    /* 监听 */
    ret = listen(sockfd, MAX_LISTEN);
    if (ret == -1)
    {
        perror("listen error");
        exit(-1);
    }

    /*创建epoll红黑树实例*/
    int epollfd = epoll_create(1);
    if (epollfd == -1)
    {
        perror("epoll_create error");
        exit(-1);
    }

    /*将sockfd添加到红黑树实例里*/
    struct epoll_event event;
    memset(&event, 0, sizeof(event));
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event);
    if (ret == -1)
    {
        perror("epoll error");
        exit(-1);
    }
    /*监听的数量*/
    int nums = 0;
    /*读到的字节数*/
    int readBytes = 0;
    char buffer[READ_SIZE] = {0};
    while (1)
    {
        struct epoll_event events[BUFFER_SIZE];
        memset(events, 0, sizeof(events));
        nums = epoll_wait(epollfd, events, BUFFER_SIZE, -1);
        if (nums == -1)
        {
            perror("epoll_wait error");
            exit(-1);
        }
        /*程序执行到这个地方有两种情况
            1.超时
            2.有监听数据来了
        */
        for (int idx = 0; idx < nums; idx++)
        {
            int fd = events[idx].data.fd;
            if (fd == sockfd)
            {
                /*有连接*/
                int connectfd = accept(sockfd, NULL, NULL);
                if (connectfd == -1)
                {
                    perror("accept error");
                    exit(-1);
                }
                /*将通信句柄fd设置成非阻塞*/
                int flag = fcntl(connectfd, F_GETFL);
                flag = O_NONBLOCK;
                fcntl(connectfd, F_SETFL, flag);

                struct epoll_event conEvent;
                memset(&conEvent, 0, sizeof(conEvent));
                conEvent.data.fd = connectfd;
                conEvent.events = EPOLLIN;
                /*将通信的句柄添加到树节点中*/
                ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, connectfd, &conEvent);
                if (ret == -1)
                {
                    perror("epoll_ctl error");
                    continue;
                }
            }
            else
            {
                readBytes = read(fd, buffer, sizeof(buffer) - 1);
                if (readBytes < 0)
                {
                    perror("read error");
                    /*将该文件句柄从红黑树上删除*/
                    epoll_ctl(fd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                }
                else if (readBytes == 0)
                {
                    printf("客户端下线。。。\n");
                    /*将该文件句柄从红黑树上删除*/
                    epoll_ctl(fd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                }
                else
                {
                    /*有数据通信*/
                    printf("client massage=%s\n", buffer);
                    for (int jdx = 0; jdx < readBytes; jdx++)
                    {
                        buffer[jdx] = toupper(buffer[jdx]);
                    }
                    /*发挥客户端*/
                    write(fd, buffer, readBytes);
                    sleep(1);
                }
            }
        }
    }
    /* 关闭文件描述符 */
    close(sockfd);

    return 0;
}